package com.odata.springboot.starter.odata.odata.sql.filterMethod;

import com.odata.springboot.starter.odata.odata.config.OdataCachedData;
import com.odata.springboot.starter.odata.odata.sql.sqlBuild.OdataClassMetaData;
import com.odata.springboot.starter.odata.odata.sql.sqlBuild.OdataSqlInformation;
import com.odata.springboot.starter.odata.odata.sql.sqlBuild.SqlSyntaxConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
import org.apache.olingo.server.core.uri.queryoption.expression.*;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author chenYX
 * @date 2021/6/29 14:42
 * @description
 */
@Component
@Slf4j
public class ParseFilterProcess {

    /**
     * 处理 filter表达式
     * @author : yongxiang
     * @description :
     * @param
     * @date : 10:07 2021/7/2
    */
    public void processFilterExpression(Expression filterExpression, OdataSqlInformation.SqlInformationBuild sqlInformationBuild) {
        if (filterExpression instanceof AliasImpl) {
            throw new IllegalArgumentException("NOT SUPPORTED:Expression:AliasImpl");
        } else if (filterExpression instanceof BinaryImpl) {
            //boolean 表达式 如 name eq 'test' ; age gt 1 等
            expandBinary((BinaryImpl) filterExpression, sqlInformationBuild);
            return;
        } else if (filterExpression instanceof EnumerationImpl) {
            throw new IllegalArgumentException("NOT SUPPORTED:Expression:EnumerationImpl");
        } else if (filterExpression instanceof LambdaRefImpl) {
            throw new IllegalArgumentException("NOT SUPPORTED:Expression:LambdaRefImpl");
        } else if (filterExpression instanceof LiteralImpl) {
            //处理 赋值 如 name eq 'slm' 处理的就是 'slm'
            expandLiteral((LiteralImpl) filterExpression, sqlInformationBuild);
            return;
        } else if (filterExpression instanceof MemberImpl) {
            //处理 过滤的条件 如 name eq 'slm' 处理的就是 name
            expandMember((MemberImpl) filterExpression, sqlInformationBuild);
            return;
        } else if (filterExpression instanceof MethodImpl) {
            //处理的是函数表达式 如 name like startswith(name,'slm')
            expandMethod((MethodImpl) filterExpression, sqlInformationBuild);
            return;
        } else if (filterExpression instanceof TypeLiteralImpl) {
            throw new IllegalArgumentException("NOT SUPPORTED:Expression:TypeLiteralImpl");
        } else if (filterExpression instanceof UnaryImpl) {
            //处理一元运算符 如 ！
            UnaryImpl impl = (UnaryImpl) filterExpression;
            expandUnary(impl, sqlInformationBuild);
            return;
        }

        final String message = "Unexpected Case: Unsupported expression:" + filterExpression.getClass().getName() + ","
                + filterExpression + "]";
        log.error(message);
        throw new IllegalArgumentException(message);
    }


    private void expandBinary(BinaryImpl impl, OdataSqlInformation.SqlInformationBuild sqlInformationBuild) {
        BinaryOperatorKind opKind = impl.getOperator();
        String sqlOperator = StringUtils.EMPTY;
        if (opKind == BinaryOperatorKind.HAS) {
            // HAS
            throw new IllegalArgumentException("NOT SUPPORTED:BinaryOperatorKind:" + opKind);
        } else if (opKind == BinaryOperatorKind.MUL) {
            // MUL
            throw new IllegalArgumentException("NOT SUPPORTED:BinaryOperatorKind:" + opKind);
        } else if (opKind == BinaryOperatorKind.DIV) {
            // DIV
            throw new IllegalArgumentException("NOT SUPPORTED:BinaryOperatorKind:" + opKind);
        } else if (opKind == BinaryOperatorKind.MOD) {
            // MOD
            throw new IllegalArgumentException("NOT SUPPORTED:BinaryOperatorKind:" + opKind);
        } else if (opKind == BinaryOperatorKind.ADD) {
            // ADD
            throw new IllegalArgumentException("NOT SUPPORTED:BinaryOperatorKind:" + opKind);
        } else if (opKind == BinaryOperatorKind.SUB) {
            // SUB
            throw new IllegalArgumentException("NOT SUPPORTED:BinaryOperatorKind:" + opKind);
        } else if (opKind == BinaryOperatorKind.GT) {
            sqlOperator = SqlSyntaxConstants.GT;
        } else if (opKind == BinaryOperatorKind.GE) {
            sqlOperator = SqlSyntaxConstants.GE;
        } else if (opKind == BinaryOperatorKind.LT) {
            sqlOperator = SqlSyntaxConstants.LT;
        } else if (opKind == BinaryOperatorKind.LE) {
            sqlOperator = SqlSyntaxConstants.LE;
        } else if (opKind == BinaryOperatorKind.EQ) {
            sqlOperator = SqlSyntaxConstants.EQ;
        } else if (opKind == BinaryOperatorKind.NE) {
            sqlOperator = SqlSyntaxConstants.NE;
        } else if (opKind == BinaryOperatorKind.AND) {
            sqlOperator = SqlSyntaxConstants.AND;
        } else if (opKind == BinaryOperatorKind.OR) {
            sqlOperator = SqlSyntaxConstants.OR;
        }

        if (StringUtils.isNotBlank(sqlOperator)) {
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.LEFT_PARENTHESIS);
            //处理左边
            processFilterExpression(impl.getLeftOperand(), sqlInformationBuild);
            //操作符
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.SPACE)
                    .appendFilter(sqlOperator)
                    .appendFilter(SqlSyntaxConstants.SPACE);
            //处理右边
            processFilterExpression(impl.getRightOperand(), sqlInformationBuild);
            //添加右括号
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.RIGHT_PARENTHESIS);
            return;
        }

        final String message = "Unexpected Case: Unsupported binary operator:" + opKind + "," + impl + "]";
        log.error(message);
        throw new IllegalArgumentException(message);
    }

    /**
     * 参数列表 filter
     *
     * @param
     * @author : yongxiang
     * @description :
     * @date : 14:02 2021/6/29
     */
    private void expandLiteral(LiteralImpl impl, OdataSqlInformation.SqlInformationBuild sqlInformationBuild) {
        String value = impl.toString();
        if (value.startsWith(SqlSyntaxConstants.APOSTROPHE) && value.endsWith(SqlSyntaxConstants.APOSTROPHE)) {
            value = value.substring(1, value.length() - 1);
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.PLACEHOLDER);
            sqlInformationBuild.appendParameter(value);
            return;
        }
        //数值类型的直接
        sqlInformationBuild.appendFilter(value);
    }

    /**
     * 添加 过滤条件属性
     *
     * @param
     * @author : yongxiang
     * @description :
     * @date : 14:17 2021/6/29
     */
    private void expandMember(MemberImpl impl, OdataSqlInformation.SqlInformationBuild sqlInformationBuild) {
        Class aClass = sqlInformationBuild.getEdmClass();
        OdataClassMetaData odataClassMetaData = OdataCachedData.cachedCsdlMetaDataMap.get(aClass.getName());
        Map<String, String> objectFieldToDataBaseFieldMap = odataClassMetaData.getObjectFieldToDataBaseFieldMap();
        String fieldName = impl.toString();
        fieldName = fieldName.substring(1, fieldName.length() - 1);
        String dataBaseField = objectFieldToDataBaseFieldMap.get(fieldName);
        sqlInformationBuild.appendFilter(dataBaseField);

    }

    /**
     * 处理函数
     *
     * @param
     * @author : yongxiang
     * @description :
     * @date : 14:18 2021/6/29
     */
    private void expandMethod(MethodImpl impl, OdataSqlInformation.SqlInformationBuild sqlInformationBuild) {

        String methodName = impl.getMethod().toString();
        FilterMethodHandler handler = FilterMethodHandlerFactory.getHandler(methodName);
        if (handler != null) {
            handler.handle(impl, sqlInformationBuild);
            return;
        }

        final String message = "Unexpected Case: NOT SUPPORTED MethodKind:" + impl.getMethod() + "," + impl.toString()
                + "]";
        throw new IllegalArgumentException(message);
    }

    private void expandUnary(UnaryImpl impl, OdataSqlInformation.SqlInformationBuild sqlInformationBuild) {
        if (impl.getOperator() == UnaryOperatorKind.NOT) {
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.LEFT_PARENTHESIS)
                    .appendFilter(SqlSyntaxConstants.NOT)
                    .appendFilter(SqlSyntaxConstants.SPACE)
                    .appendFilter(SqlSyntaxConstants.LEFT_PARENTHESIS);

            processFilterExpression(impl.getOperand(), sqlInformationBuild);
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.RIGHT_PARENTHESIS)
                    .appendFilter(SqlSyntaxConstants.RIGHT_PARENTHESIS);
            return;
        } else if (impl.getOperator() == UnaryOperatorKind.MINUS) {
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.LEFT_PARENTHESIS)
                    .appendFilter(SqlSyntaxConstants.MINUS)
                    .appendFilter(SqlSyntaxConstants.LEFT_PARENTHESIS);

            processFilterExpression(impl.getOperand(), sqlInformationBuild);
            sqlInformationBuild.appendFilter(SqlSyntaxConstants.RIGHT_PARENTHESIS)
                    .appendFilter(SqlSyntaxConstants.RIGHT_PARENTHESIS);
            return;
        }

        final String message = "Unexpected Case: Unsupported UnaryOperatorKind:" + impl.getOperator() + ","
                + impl + "]";
        log.error(message);
        throw new IllegalArgumentException(message);
    }
}
